﻿using System.Collections;
using System.Runtime.CompilerServices;
using DimensionsHelper.LanguageService.Protocol;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService;

/// <summary>
///     错误信息存储对象，此对象只管理当个语法树中的错误信息。
/// </summary>
public class DiagnosticStorage : IEnumerable<DiagnosticInfo>
{
    // 不同的语法树有单独的错误信息存储对象
    private static readonly ConditionalWeakTable<SyntaxTree, DiagnosticStorage> TreeDiagnostics = new();

    // 不同预处理文件对应的错误信息存储都对象
    private static readonly ConditionalWeakTable<ProcessedFile, DiagnosticStorage> FileDiagnostics = new();
    private readonly ConditionalWeakTable<ITextNode, DiagnosticLinkedListNode> _nodeDiagnostics = new();

    private readonly DiagnosticLinkedListNode _rootNode = new(null);
    private readonly List<DiagnosticInfo> _unLocatedDiagnostics = [];
    private readonly List<SyntaxLocation> _unreachableLocations = [];
    private readonly List<SyntaxLocation> _unusedLocations = [];

    private DiagnosticStorage()
    {
    }


    public IEnumerator<DiagnosticInfo> GetEnumerator()
    {
        return new DiagnosticEnumerator(this);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }


    /// <summary>
    ///     获取语法树绑定的报错信息存储对象。
    /// </summary>
    /// <param name="syntaxTree">指定语法树</param>
    public static DiagnosticStorage GetDiagnosticStorage(SyntaxTree syntaxTree)
    {
        if (TreeDiagnostics.TryGetValue(syntaxTree, out DiagnosticStorage? storage))
        {
            return storage;
        }

        storage = new DiagnosticStorage();
        TreeDiagnostics.Add(syntaxTree, storage);

        return storage;
    }


    /// <summary>
    ///     获取预处理文件绑定的错误信息存储对象。
    /// </summary>
    /// <param name="file">指定预处理文件</param>
    public static DiagnosticStorage GetDiagnosticStorage(ProcessedFile file)
    {
        if (FileDiagnostics.TryGetValue(file, out DiagnosticStorage? storage))
        {
            return storage;
        }

        storage = new DiagnosticStorage();
        FileDiagnostics.Add(file, storage);

        return storage;
    }


    /// <summary>
    ///     获取指定语法节点上的错误信息
    /// </summary>
    /// <param name="textNode">语法节点</param>
    public IReadOnlyList<DiagnosticInfo> GetDiagnostics(ITextNode textNode)
    {
        return _nodeDiagnostics.TryGetValue(textNode, out var linkedListNode) ? linkedListNode.GetDiagnostics() : [];
    }


    /// <summary>
    ///     获取未使用（预处理条件编译指令判定未通过的部分）文本区间。
    /// </summary>
    public IReadOnlyList<SyntaxLocation> GetUnusedSpans()
    {
        return _unusedLocations.AsReadOnly();
    }


    /// <summary>
    ///     获取无法访问的代码区间。
    /// </summary>
    public IReadOnlyList<SyntaxLocation> GetUnreachableSpans()
    {
        return _unreachableLocations.AsReadOnly();
    }


    /// <summary>
    ///     添加一个无法访问的代码文本区间。
    /// </summary>
    /// <param name="uri">文本文件URI</param>
    /// <param name="span">文本区间</param>
    public void Unreachable(DocumentUri uri, in TextSpan span)
    {
        _unreachableLocations.Add(SyntaxLocation.CreateUnreachable(uri, span.Start, span.Width));
    }


    /// <summary>
    ///     添加一个区间到未使用的文本区间列表。
    /// </summary>
    /// <param name="uri">文本文件URI</param>
    /// <param name="span">文本区间</param>
    public void UnUse(DocumentUri uri, in TextSpan span)
    {
        _unusedLocations.Add(SyntaxLocation.CreateUnused(uri, span.Start, span.Width));
    }


    /// <summary>
    ///     报告一个错误信息到错误表中。
    /// </summary>
    /// <param name="info">指定错误信息</param>
    public void Raise(DiagnosticInfo info)
    {
        _unLocatedDiagnostics.Add(info);
    }


    /// <summary>
    ///     将不是空的错误链合并成一个错误信息并添加到错误表中。
    /// </summary>
    /// <param name="diagnosticChain">错误链信息</param>
    public void Raise(DiagnosticChain diagnosticChain)
    {
        if (!diagnosticChain.IsEmpty)
        {
            Raise(diagnosticChain.GetDiagnostic());
        }
    }


    /// <summary>
    ///     在指定的文本节点位置报告错误。
    /// </summary>
    /// <param name="descriptor">错误模板信息</param>
    /// <param name="location">文本区间</param>
    /// <param name="args">模板信息格式化参数</param>
    public void Raise(DiagnosticDescriptor descriptor, in SyntaxLocation location,
        params object?[] args)
    {
        Raise(new DiagnosticInfo(location, descriptor, args));
    }


    /// <summary>
    ///     在指定的文本节点位置报告错误。
    /// </summary>
    /// <param name="descriptor">错误模板信息</param>
    /// <param name="span">文本区间</param>
    /// <param name="args">模板信息格式化参数</param>
    public void Raise(DiagnosticDescriptor descriptor, in TextSpan span, params object?[] args)
    {
        Raise(new DiagnosticInfo(SyntaxLocation.Create(span.Uri, span.Start, span.Width), descriptor, args));
    }


    /// <summary>
    ///     在指定的文本节点位置报告错误。
    /// </summary>
    /// <param name="diagnostic">错误模板信息</param>
    /// <param name="node">文本节点</param>
    /// <param name="args">模板信息格式化参数</param>
    public void Raise(DiagnosticDescriptor diagnostic, ITextNode node, params object?[] args)
    {
        DiagnosticInfo info = new(
            SyntaxLocation.Create(node.Uri, node.Start, node.Width), diagnostic, args);

        if (!_nodeDiagnostics.TryGetValue(node, out DiagnosticLinkedListNode? diagnosticsNode))
        {
            diagnosticsNode = new DiagnosticLinkedListNode(node);
            _nodeDiagnostics.Add(node, diagnosticsNode);
            // 添加到链表后
            DiagnosticLinkedListNode lastNode = _rootNode.Prev;
            lastNode.Next = diagnosticsNode;
            diagnosticsNode.Prev = lastNode;
            diagnosticsNode.Next = _rootNode;
            _rootNode.Prev = diagnosticsNode;
        }

        diagnosticsNode.AddDiagnostic(info);
    }


    /// <summary>
    ///     清空所有已保存的错误信息。
    /// </summary>
    public void Clear()
    {
        _nodeDiagnostics.Clear();
        _unusedLocations.Clear();
    }


    private class DiagnosticLinkedListNode
    {
        private readonly List<DiagnosticInfo> _diagnostics = [];

        private readonly WeakReference<ITextNode>? _nodeReference;


        public DiagnosticLinkedListNode(ITextNode? node)
        {
            if (node != null)
            {
                _nodeReference = new WeakReference<ITextNode>(node);
            }

            Prev = this;
            Next = this;
        }

        public DiagnosticLinkedListNode Prev { get; set; }

        public DiagnosticLinkedListNode Next { get; set; }

        public ITextNode? GetReference()
        {
            return _nodeReference?.TryGetTarget(out ITextNode? reference) == true ? reference : null;
        }

        public IReadOnlyList<DiagnosticInfo> GetDiagnostics()
        {
            return _diagnostics;
        }

        public void AddDiagnostic(DiagnosticInfo diagnostic)
        {
            _diagnostics.Add(diagnostic);
        }
    }


    private struct DiagnosticEnumerator(DiagnosticStorage storage) : IEnumerator<DiagnosticInfo>
    {
        private readonly DiagnosticLinkedListNode _rootNode = storage._rootNode;
        private DiagnosticLinkedListNode _currentNode = storage._rootNode;
        private int _currentIndex = -1;
        private IReadOnlyList<DiagnosticInfo> _currentList = storage._unLocatedDiagnostics;
        private bool _withinUnLocatedDiagnostics = true;

        public bool MoveNext()
        {
            while (true)
            {
                // 先遍历不在节点上的错误
                if (_withinUnLocatedDiagnostics)
                {
                    _currentIndex++;

                    if (_currentIndex >= _currentList.Count)
                    {
                        _withinUnLocatedDiagnostics = false;
                        continue;
                    }

                    Current = _currentList[_currentIndex];
                    return true;
                }

                if (_rootNode == _currentNode || _currentIndex >= _currentList.Count ||
                    _currentNode.GetReference() == null)
                {
                    _currentNode = _currentNode.Next;
                    _currentIndex = -1;
                    _currentList = _currentNode.GetDiagnostics();
                }

                if (_currentNode == _rootNode)
                {
                    return false;
                }

                _currentIndex++;
                if (_currentIndex >= _currentList.Count && !MoveNext())
                {
                    return false;
                }

                Current = _currentList[_currentIndex];
                return true;
            }
        }

        public void Reset()
        {
            _currentNode = _rootNode;
            _currentIndex = -1;
            _currentList = _currentNode.GetDiagnostics();
        }

        public DiagnosticInfo Current { get; private set; } = null!;

        object? IEnumerator.Current => Current;

        public void Dispose()
        {
        }
    }
}


public static class SyntaxExtensions
{
    /// <summary>
    ///     获取语法树对应的错误存储对象
    /// </summary>
    public static DiagnosticStorage GetDiagnosticStorage(this SyntaxTree syntaxTree)
    {
        return DiagnosticStorage.GetDiagnosticStorage(syntaxTree);
    }


    /// <summary>
    ///     获取预处理文件对应的错误存储对象
    /// </summary>
    public static DiagnosticStorage GetDiagnosticStorage(this ProcessedFile file)
    {
        return DiagnosticStorage.GetDiagnosticStorage(file);
    }
}